#ifndef __SYMBOLTABLE_H__
#define __SYMBOLTABLE_H__

#include <assert.h>
#include <map>
#include <string>
#include <vector>

class Type;
class Operand;

class SymbolEntry {
   private:
    int kind;

   protected:
    enum { CONSTANT, VARIABLE, TEMPORARY };
    Type* type;
    //增加同一名称，不同标识符的识别（函数和变量）
    SymbolEntry* next;
   public:
    SymbolEntry(Type* type, int kind);
    virtual ~SymbolEntry(){};
    bool isConstant() const { return kind == CONSTANT; };
    bool isTemporary() const { return kind == TEMPORARY; };
    bool isVariable() const { return kind == VARIABLE; };
    Type* getType() { return type; };
    void setType(Type *type) {this->type = type;};
    virtual std::string toStr() = 0;
    bool setNext(SymbolEntry* se);
    SymbolEntry* getNext() const { return next; };
    // You can add any function you need here.
};

class SymbolTable {
   public:
    std::map<std::string, SymbolEntry*> symbolTable;
    SymbolTable* prev;
    int level;
    static int counter;
    SymbolTable();
    SymbolTable(SymbolTable* prev);
    void install(std::string name, SymbolEntry* entry);
    SymbolEntry* lookup(std::string name);
    SymbolEntry* lookup_function(std::string name,std::vector<Type*> argu_type);
    SymbolTable* getPrev() { return prev; };
    int getLevel() { return level; };
    static int getLabel() { return counter++; };
};
/*
    Symbol entry for literal constant. Example:

    int a = 1;

    Compiler should create constant symbol entry for literal constant '1'.
*/
class ConstantSymbolEntry : public SymbolEntry {
   private:
    int value;
    std::string strValue;

   public:
    ConstantSymbolEntry(Type* type, int value);
    ConstantSymbolEntry(Type* type, std::string strValue);
    ConstantSymbolEntry(Type* type);
    virtual ~ConstantSymbolEntry(){};
    int getValue() const;
    std::string getStrValue() const;
    std::string toStr();
    // You can add any function you need here.
};

/*
    Symbol entry for identifier. Example:

    int a;
    int b;
    void f(int c)
    {
        int d;
        {
            int e;
        }
    }

    Compiler should create identifier symbol entries for variables a, b, c, d
   and e:

    | variable | scope    |
    | a        | GLOBAL   |
    | b        | GLOBAL   |
    | c        | PARAM    |
    | d        | LOCAL    |
    | e        | LOCAL +1 |
*/
class IdentifierSymbolEntry : public SymbolEntry {
   private:
    enum { GLOBAL, PARAM, LOCAL };
    std::string name;
    int scope;
    int value;
    bool initial;
    bool sysf;
    bool constant=false;
    int* arrayValue;
    int paramNo;
    //增加符号作为操作数的信息
    Operand *addr;
    // You can add any field you need here.

   public:
   int label;
    IdentifierSymbolEntry(Type* type, std::string name, int scope,int paramNo=-1,bool sysy = false);
    virtual ~IdentifierSymbolEntry(){};
    std::string toStr();
    //增加判断作用域的内联函数
    bool isGlobal() const {return scope == GLOBAL;};
    bool isParam() const {return scope == PARAM;};
    bool isLocal() const {return scope >= LOCAL;};
    int getScope() const { return scope; };
    void setValue(int value);
    int getValue() const { return value; };
    void setArrayValue(int* arrayValue);
    int* getArrayValue() const { return arrayValue; };
    int getLabel() const { return label; };
    void setLabel() { label = SymbolTable::getLabel(); };
    //增加操作数信息的操作
    Operand* getAddr() {return addr;}
    void setAddr(Operand *addr) {this->addr = addr;};
    bool isSysf() const { return sysf; };
    // You can add any function you need here.
    bool getConst() const { return constant; };
    void setConst() {this->constant=true;};
    int getParamNo() const { return paramNo; };
};

/*
    Symbol entry for temporary variable created by compiler. Example:

    int a;
    a = 1 + 2 + 3;

    The compiler would generate intermediate code like:

    t1 = 1 + 2
    t2 = t1 + 3
    a = t2

    So compiler should create temporary symbol entries for t1 and t2:

    | temporary variable | label |
    | t1                 | 1     |
    | t2                 | 2     |
*/
class TemporarySymbolEntry : public SymbolEntry {
   private:
    int label;
    int stack_offset;
   public:
    TemporarySymbolEntry(Type* type, int label);
    virtual ~TemporarySymbolEntry(){};
    std::string toStr();
    // You can add any function you need here.
    int getLabel() const {return label;};
    void setOffset(int offset) { this->stack_offset = offset; };
    int getOffset() { return this->stack_offset; };
};

// symbol table managing identifier symbol entries


extern SymbolTable* identifiers;
extern SymbolTable* globals;

#endif
